// dotnet $DOTNET_CSC_DLL -nologo -t:library -r:"../../The Scroll of Taiwu_Data/Managed/System.dll" -r:"../../The Scroll of Taiwu_Data/Managed/netstandard.dll" -r:"../../The Scroll of Taiwu_Data/Managed/System.Reflection.Emit.ILGeneration.dll" -r:"../../The Scroll of Taiwu_Data/Managed/0Harmony.dll" -r:"../../The Scroll of Taiwu_Data/Managed/mscorlib.dll" -r:"../../The Scroll of Taiwu_Data/Managed/Assembly-CSharp.dll" -r:"../../The Scroll of Taiwu_Data/Managed/TaiwuModdingLib.dll" -r:"../../The Scroll of Taiwu_Data/Managed/Unity.TextMeshPro.dll" -r:"../../The Scroll of Taiwu_Data/Managed/UnityEngine.CoreModule.dll" -r:"../../The Scroll of Taiwu_Data/Managed/UnityEngine.UI.dll" -r:"../../The Scroll of Taiwu_Data/Managed/UnityEngine.dll"  -r:"../../The Scroll of Taiwu_Data/Managed/System.Core.dll" -r:"../../The Scroll of Taiwu_Data/Managed/Newtonsoft.Json.dll" -optimize -unsafe -deterministic -debug NeutronFrontend-DataDumper.cs Utils.CS -out:NeutronFrontend-DataDumper.dll
// -r:"../../The Scroll of Taiwu_Data/Managed/Mono.Cecil.dll" -r:"../../The Scroll of Taiwu_Data/Managed/System.Composition.AttributedModel.dll"
/**
 *  Everyone's Unity Game Plugin
 *  Copyright (C) 2022 Neutron3529
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
#define export_NameList

using System;
using System.Reflection;
using System.Reflection.Emit;
using System.Collections.Generic;

using System.Linq;
using Newtonsoft.Json;

using Config;
using Config.ConfigCells.Character;

namespace DataDumper;
using Utils;
using static Utils.Logger;
[TaiwuModdingLib.Core.Plugin.PluginConfig("DataDumper","Neutron3529","0.1.0")]

public class DataDumper : TaiwuModdingLib.Core.Plugin.TaiwuRemakePlugin {
    private bool NForceOFFEnable(){
        bool enable=false;
        if(!ModManager.GetSetting(this.ModIdStr, "NForceOFF",ref enable) || !enable){
            GameData.Domains.Mod.ModInfo modInfo;
            ((Dictionary<string, GameData.Domains.Mod.ModInfo>)(typeof(ModManager).GetField("_localMods",(BindingFlags)(-1))).GetValue(null)).TryGetValue(this.ModIdStr, out modInfo);
            System.IO.File.WriteAllText(System.IO.Path.Combine(modInfo.DirectoryName, "Settings.Lua"),"return{NForceOFF=true}");
            return false;
        }
        return true;
    }
    public override void Initialize(){
        if(NForceOFFEnable())return;
        System.IO.Directory.CreateDirectory("csvData");
        System.IO.File.WriteAllText("csvData/SkillBreakGridList.csv","#"+","+"名称"+","+"承"+","+"合"+","+"解"+","+"异"+","+"独\n");
        foreach(SkillBreakGridListItem item in SkillBreakGridList.Instance){
            System.IO.File.AppendAllText("csvData/SkillBreakGridList.csv",item.TemplateId+","+CombatSkill.Instance[item.TemplateId].Name+","+breakGrid(item.BreakGridListJust)+","+breakGrid(item.BreakGridListKind)+","+breakGrid(item.BreakGridListEven)+","+breakGrid(item.BreakGridListRebel)+","+breakGrid(item.BreakGridListEgoistic)+"\n");
        }
        System.IO.File.WriteAllText("csvData/SkillBreakPlateGridBonusType.csv","#"+","+"名称"+","+"人物修正"+","+"技能修正"+"\n");
        foreach(SkillBreakPlateGridBonusTypeItem item in SkillBreakPlateGridBonusType.Instance){
            System.IO.File.AppendAllText("csvData/SkillBreakPlateGridBonusType.csv",item.TemplateId.ToString()+","+item.Name+","+CharacterPropertyAndValue(item.CharacterPropertyBonusList)+","+CombatPropertyAndValue(item.CombatSkillPropertyBonusList)+"\n");
        }
#if export_NameList
        var q = from t in typeof(Config.Adventure).Assembly.GetTypes()
        where t.IsClass && t.Namespace == typeof(Config.Adventure).Namespace
        select t;

        string str="";

        var nm=new List<string>(from t in q select t.Name);
        foreach(var target in q){
//            str+="//        search for type "+target.Name+"\n";
            if(nm.Contains(target.Name+"Item")){
//                str+="//        found legal type "+target.Name+"\n";
                if(target.GetField("Instance") !=null){
//                    str+="//        ProcessInstance("+target.Name+".Instance);\n";
                    var v=target.GetField("Instance").GetValue(null);
                    if(v!=null)str+="        ProcessInstance(Config."+target.Name+".Instance);\n";
                }
            }
        }
        System.IO.File.WriteAllText("csvData/NameList[auto].txt",str);
#endif
        var processInstance=typeof(DataDumper).GetMethod("ProcessInstance");
        foreach(var target in q){
//            str+="//        search for type "+target.Name+"\n";
            if(nm.Contains(target.Name+"Item")){
//                str+="//        found legal type "+target.Name+"\n";
                if(target.GetField("Instance") !=null){
//                    str+="//        ProcessInstance("+target.Name+".Instance);\n";
                    Type type=typeof(object);
                    try {
                        type=(new List<Type>(from t in target.GetMethods((BindingFlags)(-1)) where t.Name == "GetItem" select t.ReturnType))[0];//target.GetMethod("GetItem").ReturnType;
                    } catch(Exception e){
                        logex(e);
                        logwarn("这是搜索"+target.Name+"数据时出了问题，此BUG不影响其他数据的导出工作");
                        continue;
                    }
                    var v=target.GetField("Instance").GetValue(null);
                    try{
                        if(v!=null){
                            processInstance.MakeGenericMethod(type).Invoke(null, new object[]{v,null});
                        }
                    } catch(Exception e){
                        logex(e);
                        logwarn("导出"+target.Name+"数据存在问题，此BUG不影响其他数据的导出工作");
                        continue;
                    }
                }
            }
        }
        //this.OnModSettingUpdate(); 每次游戏加载之后会自动调用更新参数的patch
        //GameData.Domains.Mod.ModInfo modInfo;
        //((Dictionary<string, GameData.Domains.Mod.ModInfo>)(typeof(ModManager).GetField("_localMods",(BindingFlags)(-1))).GetValue(null)).TryGetValue(this.ModIdStr, out modInfo);
        //modInfo.ModSettingEntries.Add(new FrameWork.ModSystem.ToggleSetting("key", "测试", "说明", false));
    }

    public override void Dispose() {
    }

    public static void ProcessInstance<T>(IEnumerable<T> instance, Type indexType=null){ // instance[0].GetType()
        var content=new System.Text.StringBuilder("");
        var name=instance.GetType().Name+"[auto]";
        if(indexType==null){

            foreach(var x in instance.GetType().GetProperties((BindingFlags)(-1))){
                if(x.Name=="Item"){
                    indexType=x.GetGetMethod().ReturnType;
                }
                break;
            }
            if(indexType==null)throw new Exception("类"+name+"中不存在合适的get_Item函数，应自行写入合适的type");
        }
        try{
            //var instance=Character.Instance;
            var fields=indexType.GetFields();
            foreach(var field in fields){
                content.Append(field.Name+",");
            }
            content.Append("\n");
            int line=0;
            foreach(var item in instance){
                if(item!=null){
                    foreach(var field in fields){
                        content.Append(ItemField(item,field,line));
                    }
                } else {
                    content.Append($"{line},null readed.");
                }
                line++;
                content.Append("\n");
            }
        }catch(Exception e){
            System.IO.File.WriteAllText(name+".err.txt",_strexception(e,0));
            throw;
        }finally{
            System.IO.File.WriteAllText("csvData/"+name+".csv",content.Replace(",\n","\n").ToString());
        }
    }
    public static MethodInfo _convert=typeof(DataDumper).GetMethod("convert");
    public static MethodInfo _Sconvert=typeof(DataDumper).GetMethod("Sconvert");
    public static string ItemField<T>(T item, FieldInfo field, int Line){
        var x=field.GetValue(item);
        if(x==null){
            return ",";
        }else if (field.FieldType == typeof(GameData.Domains.Item.PoisonsAndLevels)){
            return PoisonsAndLevels((GameData.Domains.Item.PoisonsAndLevels)x)+",";
        } else if(field.FieldType == typeof(string)){
            return ((string)x).Replace("\n","\\n").Replace(",","，")+",";
        }
        foreach(var ifce in new Type[]{typeof(byte),typeof(sbyte),typeof(ushort),typeof(short),typeof(uint),typeof(int)}){
            if (typeof(IEnumerable<>).MakeGenericType(ifce).IsAssignableFrom(field.FieldType)){
                var ele=field.FieldType.GetElementType();
                if(ele==null)ele=field.FieldType.GetGenericArguments()[0];
                return ((string)_convert.MakeGenericMethod(ele).Invoke(null,new object[]{x}))+",";
            }
        }
        foreach(var ifce in new Type[]{typeof(string),typeof(Config.ConfigCells.Character.PropertyAndValue),typeof(Config.ConfigCells.Character.PresetInventoryItem),typeof(Config.ConfigCells.Character.PresetEquipmentItem),typeof(Config.ConfigCells.Character.PresetEquipmentItemWithProb)}){
            if (typeof(IEnumerable<>).MakeGenericType(ifce).IsAssignableFrom(field.FieldType)){
                var ele=field.FieldType.GetElementType();
                if(ele==null)ele=field.FieldType.GetGenericArguments()[0];
                return ((string)_Sconvert.MakeGenericMethod(ele).Invoke(null,new object[]{x}))+",";
            }
        }

        if(field.FieldType.GetField("Items") != null){
            var y=(System.Runtime.CompilerServices.FixedBufferAttribute)Attribute.GetCustomAttribute(field.FieldType.GetField("Items"),typeof(System.Runtime.CompilerServices.FixedBufferAttribute));
            var ser=field.FieldType.GetMethod("Serialize",(BindingFlags)(-1),null,new Type[]{typeof(byte*)},null);
            var mgm=typeof(cvtr<>).MakeGenericType(new Type[]{y.ElementType});
            if(y!=null && ser!=null && mgm!=null){
                var cvt=mgm.GetMethod("structConvert");
                if(cvt!=null){
                    return (string)(cvt.Invoke(null, new object[]{x,y.Length,ser}))+",";
                }
            }
        }
        try {
            var ret=JsonConvert.SerializeObject(x).Replace(",","，");
            if(field.FieldType.IsEnum){
                ret=x.ToString()+" = "+ret;
            }
            return ret+",";
        } catch {
            return x.ToString()+",";
        }
    }
    static string[] POISON_LEVEL={"无毒","生毒","剧毒","奇毒"};
    public static unsafe string PoisonsAndLevels(GameData.Domains.Item.PoisonsAndLevels x){
        var s="";
        short* ptr = x.Values;
        sbyte* ptr2 = x.Levels;
        for(int i=0;i<5;i++)s+=ptr[i].ToString()+"("+POISON_LEVEL[ptr2[i]]+")，";
        s+=ptr[5].ToString()+"("+POISON_LEVEL[ptr2[5]]+")";
        return s;
    }

    public static Dictionary<int,string> item_dict =
        (from i in Weapon.Instance select (((byte)i.ItemType<<16)|(ushort)i.TemplateId, i.Name)).Concat
        (from i in Armor.Instance select (((byte)i.ItemType<<16)|(ushort)i.TemplateId, i.Name)).Concat
        (from i in Accessory.Instance select (((byte)i.ItemType<<16)|(ushort)i.TemplateId, i.Name)).Concat
        (from i in Clothing.Instance select (((byte)i.ItemType<<16)|(ushort)i.TemplateId, i.Name)).Concat
        (from i in Carrier.Instance select (((byte)i.ItemType<<16)|(ushort)i.TemplateId, i.Name)).Concat
        (from i in Config.Material.Instance select (((byte)i.ItemType<<16)|(ushort)i.TemplateId, i.Name)).Concat
        (from i in CraftTool.Instance select (((byte)i.ItemType<<16)|(ushort)i.TemplateId, i.Name)).Concat
        (from i in Food.Instance select (((byte)i.ItemType<<16)|(ushort)i.TemplateId, i.Name)).Concat
        (from i in Medicine.Instance select (((byte)i.ItemType<<16)|(ushort)i.TemplateId, i.Name)).Concat
        (from i in TeaWine.Instance select (((byte)i.ItemType<<16)|(ushort)i.TemplateId, i.Name)).Concat
        (from i in SkillBook.Instance select (((byte)i.ItemType<<16)|(ushort)i.TemplateId, i.Name)).Concat
        (from i in Cricket.Instance select (((byte)i.ItemType<<16)|(ushort)i.TemplateId, i.Name)).Concat
        (from i in Misc.Instance select (((byte)i.ItemType<<16)|(ushort)i.TemplateId, i.Name)).ToDictionary(i=>i.Item1,i=>i.Item2);

    public static string item_name(int x){
        string res;
        if (item_dict.TryGetValue(x,out res)){return res;}
        if((x&65535)==65535){
            return "无";
        }else{
            UnityEngine.Debug.LogError($"找不到item_name为{x.ToString("X8")}的物品");
            return "failed";
        }
    }
    public static unsafe string PresetEquipmentItem(Config.ConfigCells.Character.PresetEquipmentItem x){
        return $"{item_name((x.Type<<16) | (ushort)x.TemplateId)}";
    }
    public static unsafe string PresetEquipmentItemWithProb(Config.ConfigCells.Character.PresetEquipmentItemWithProb x){
        return $"{item_name((x.Type<<16) | (ushort)x.TemplateId)}({x.Prob}%)";
    }
    public static unsafe string PresetInventoryItem(Config.ConfigCells.Character.PresetInventoryItem x){
        return $"{item_name((x.Type<<16) | (ushort)x.TemplateId)}x{x.Amount}({x.SpawnChance}%)";
    }

    public static class cvtr<T> where T: unmanaged {
        public static string structConvert(object item, int count, MethodInfo ser){
            if(item==null)return "";
            string s="";
            T[] array=new T[count+100];
            ser.Invoke(item,new object[]{System.Runtime.InteropServices.Marshal.UnsafeAddrOfPinnedArrayElement(array,0)});
            for(int i=0;i<count;i++){
                s+=array[i].ToString()+" ";
            }
            return s;
        }
    }
    static readonly char[] _trim_chars=new char[]{' ','\n'};
    public static string Sconvert<T>(IEnumerable<T> item){
        if(item==null)return "";
        string s="";
        if(typeof(T)==typeof(Config.ConfigCells.Character.PropertyAndValue))foreach(var x in item){
            s+=((Config.ConfigCells.Character.PropertyAndValue)(object)x).PropertyId.ToString()+"="+((Config.ConfigCells.Character.PropertyAndValue)(object)x).Value+" ";
        } else if(typeof(T)==typeof(string))foreach(var x in item){
            s+=((string)(object)x).Replace(",","，").Replace("\n","\\n")+" ";
        } else if(typeof(T)==typeof(Config.ConfigCells.Character.PresetEquipmentItem))foreach(var x in item){
            s+=PresetEquipmentItem((Config.ConfigCells.Character.PresetEquipmentItem)(object)x)+" ";
        } else if(typeof(T)==typeof(Config.ConfigCells.Character.PresetEquipmentItemWithProb))foreach(var x in item){
            s+=PresetEquipmentItemWithProb((Config.ConfigCells.Character.PresetEquipmentItemWithProb)(object)x)+" ";
        } else if(typeof(T)==typeof(Config.ConfigCells.Character.PresetInventoryItem))foreach(var x in item){
            s+=PresetInventoryItem((Config.ConfigCells.Character.PresetInventoryItem)(object)x)+" ";
        }
        return s.Trim(_trim_chars);
    }
    public static string convert<T>(IEnumerable<T> item){
        if(item==null)return "";
        string s="";
        foreach(var x in item){
            s+=x.ToString()+" ";
        }
        return s.Trim();
    }
    public static string CharacterPropertyAndValue(PropertyAndValue[] arr){
        string result="";
        foreach(PropertyAndValue g in arr){
            result+=" "+CharacterPropertyDisplay.Instance[g.PropertyId].Name+(g.Value>0?"+":"")+g.Value+(CharacterPropertyDisplay.Instance[g.PropertyId].IsPercent?"%":"");
        }
        return result;
    }
    public static string CombatPropertyAndValue(PropertyAndValue[] arr){
        string result="";
        foreach(PropertyAndValue g in arr){
            result+=" "+CombatSkillProperty.Instance[g.PropertyId].Name+(g.Value>0?"+":"")+g.Value+(CombatSkillProperty.Instance[g.PropertyId].IsPercent?"%":"");
        }
        return result;
    }
    public static string breakGrid(List<BreakGrid> lst){
        string result="";
        foreach(BreakGrid g in lst){
            result+=" "+SkillBreakPlateGridBonusType.Instance[g.BonusType].Name+"x"+g.GridCount;
        }
        return result;
    }
}
